﻿using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Lombiq.HelpfulLibraries.Common.Utilities;

/// <summary>
/// 简化 XML 序列化的实用类。
/// </summary>
public static class XmlHelper
{
    /// <summary>
    /// 将提供的 <paramref name="xml"/> 反序列化为预期的 <paramref name="outputType"/>。如果 <paramref name="xml"/> 为 null 或空，则通过返回 <see langword="default"/> 来简化处理。
    /// </summary>
    /// <exception cref="InvalidOperationException">反序列化期间发生错误。</exception>
    public static object Deserialize(string xml, Type outputType, params Type[] extraTypes)
    {
        if (string.IsNullOrEmpty(xml)) return default;

        var serializer = new XmlSerializer(outputType, extraTypes);

        using var stringReader = new StringReader(xml);
        using var xmlReader = XmlReader.Create(stringReader);

        return serializer.Deserialize(xmlReader);
    }

    /// <summary>
    /// 使用 <see cref="Deserialize"/> 将提供的 <paramref name="xml"/> 反序列化为预期类型。如果要返回的值不是 <typeparamref name="T"/>，则返回 <see langword="default"/>。
    /// </summary>
    /// <exception cref="InvalidOperationException">反序列化期间发生错误。</exception>
    public static T Deserialize<T>(string xml) => Deserialize(xml, typeof(T)) is T result ? result : default;

    /// <summary>
    /// 尝试使用 <see cref="Deserialize"/> 将提供的 <paramref name="xml"/> 反序列化为 <paramref name="result"/>。
    /// </summary>
    /// <returns>
    /// 如果反序列化成功，则返回 <see langword="true"/>；如果结果值不是 <typeparamref name="T"/> 或引发异常，则返回 <see langword="false"/>。
    /// </returns>
    public static bool TryDeserialize<T>(string xml, out T result)
    {
        result = default;

        try
        {
            if (Deserialize(xml, typeof(T)) is not T deserialized) return false;

            result = deserialized;
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 序列化 <paramref name="data"/>。
    /// </summary>
    public static string Serialize<T>(T data, params Type[] extraTypes)
    {
        var serializer = new XmlSerializer(typeof(T), extraTypes);
        var stringBuilder = new StringBuilder();

        using var writer = new StringWriter(stringBuilder);
        serializer.Serialize(writer, data);

        return stringBuilder.ToString();
    }
}
